#ifndef TYPE_ALIASES_HPP
#define TYPE_ALIASES_HPP

#include "globals.hpp"
#include <Eigen/Dense>
#include <vector>

namespace hms {

using std::string_literals::operator""s;

/* scalar is the data type used in calculations and can be changed according to
 * the precision needed. Tested with "float" (some tests fail due to lack of
 * accuracy) and "double".
 */
using scalar = double;
	
/* for consistency, and possibly to avoid conversion between signed and 
 * unsigned ints for indices, the same type (typically long long)
 * as within the Eigen library is used */
using Eigen::Index;
/* when std::vector is concerned, the type is usually unsigned */
using uIndex = std::vector<int>::size_type;

/* these types/vals will be used throughout, so they may well be abbreviated */
using Eigen::Matrix;
using Eigen::Array;
using Eigen::Dynamic;
using Eigen::Ref;
using Eigen::NoChange;
using Eigen::Map;
using Eigen::seq;
using Eigen::seqN;
using Eigen::indexing::all;

/* we're extending Eigen's naming scheme for convenience typedefs, with one
 * important difference: suffix 'i' now means "integral", or "Index", not 
 * "integer". On this machine, at the time of writing, it resolves to type 
 * "long int". This also means, that "VectorXi" and "Eigen::VectorXi" are now
 * possibly different types, so don't mix them.
 * Suffix 's' is for "scalar", which currently resolves to "double".
 */

/* "MatrixNN" is simply "Matrix", so not needed */

template<Index rows, Index cols>
using MatrixNNs = Matrix<scalar, rows, cols   >;

template<Index rows, Index cols>
using MatrixNNi = Matrix<Index , rows, cols   >;


template<typename T, Index rows>
using MatrixNX  = Matrix<T     , rows, Dynamic>;

template<Index rows>
using MatrixNXs = Matrix<scalar, rows, Dynamic>;

template<Index rows>
using MatrixNXi = Matrix<Index , rows, Dynamic>;


template<typename T>
using MatrixXX  = Matrix<T     , Dynamic, Dynamic>;

using MatrixXXs = Matrix<scalar, Dynamic, Dynamic>;
using MatrixXXi = Matrix<Index , Dynamic, Dynamic>;


template<typename T>
using Matrix2X  = Matrix<T     , 2, Dynamic>;
template<typename T>
using Matrix3X  = Matrix<T     , 3, Dynamic>;
template<typename T>
using Matrix4X  = Matrix<T     , 4, Dynamic>;

using Matrix2Xs = Matrix<scalar, 2, Dynamic>;
using Matrix3Xs = Matrix<scalar, 3, Dynamic>;
using Matrix4Xs = Matrix<scalar, 4, Dynamic>;

using Matrix2Xi = Matrix<Index , 2, Dynamic>;
using Matrix3Xi = Matrix<Index , 3, Dynamic>;
using Matrix4Xi = Matrix<Index , 4, Dynamic>;

template<Index cols>
using Matrix2Ns = Matrix<scalar, 2, cols>;
template<Index cols>
using Matrix3Ns = Matrix<scalar, 3, cols>;
template<Index cols>
using Matrix4Ns = Matrix<scalar, 4, cols>;

template<Index cols>
using Matrix2Ni = Matrix<Index , 2, cols>;
template<Index cols>
using Matrix3Ni = Matrix<Index , 3, cols>;
template<Index cols>
using Matrix4Ni = Matrix<Index , 4, cols>;

using Matrix2s  = Matrix<scalar, 2, 2>;
using Matrix3s  = Matrix<scalar, 3, 3>;
using Matrix4s  = Matrix<scalar, 4, 4>;

using Matrix2i  = Matrix<Index , 2, 2>;
using Matrix3i  = Matrix<Index , 3, 3>;
using Matrix4i  = Matrix<Index , 4, 4>;


template<typename T, Index rows>
using VectorN   = Matrix<T     , rows, 1>;

template<Index rows>
using VectorNs  = Matrix<scalar, rows, 1>;

template<Index rows>
using VectorNi  = Matrix<Index , rows, 1>;


template<typename T>
using VectorX   = Matrix<T     , Dynamic, 1>;

using VectorXs  = Matrix<scalar, Dynamic, 1>;

using VectorXi  = Matrix<Index , Dynamic, 1>;

template<typename T>
using Vector1   = Matrix<T     , 1, 1>;
template<typename T>
using Vector2   = Matrix<T     , 2, 1>;
template<typename T>
using Vector3   = Matrix<T     , 3, 1>;
template<typename T>
using Vector4   = Matrix<T     , 4, 1>;

using Vector1s  = Matrix<scalar, 1, 1>;
using Vector2s  = Matrix<scalar, 2, 1>;
using Vector3s  = Matrix<scalar, 3, 1>;
using Vector4s  = Matrix<scalar, 4, 1>;

using Vector1i  = Matrix<Index , 1, 1>;
using Vector2i  = Matrix<Index , 2, 1>;
using Vector3i  = Matrix<Index , 3, 1>;
using Vector4i  = Matrix<Index , 4, 1>;


template<typename T, Index rows>
using RowVectorN   = Matrix<T     , 1, rows>;

template<Index rows>
using RowVectorNs  = Matrix<scalar, 1, rows>;

template<Index rows>
using RowVectorNi  = Matrix<Index , 1, rows>;


template<typename T>
using RowVectorX   = Matrix<T     , 1, Dynamic>;

using RowVectorXs  = Matrix<scalar, 1, Dynamic>;

using RowVectorXi  = Matrix<Index , 1, Dynamic>;

template<typename T>
using RowVector1   = Matrix<T     , 1, 1>;
template<typename T>
using RowVector2   = Matrix<T     , 1, 2>;
template<typename T>
using RowVector3   = Matrix<T     , 1, 3>;
template<typename T>
using RowVector4   = Matrix<T     , 1, 4>;

using RowVector1s  = Matrix<scalar, 1, 1>;
using RowVector2s  = Matrix<scalar, 1, 2>;
using RowVector3s  = Matrix<scalar, 1, 3>;
using RowVector4s  = Matrix<scalar, 1, 4>;

using RowVector1i  = Matrix<Index , 1, 1>;
using RowVector2i  = Matrix<Index , 1, 2>;
using RowVector3i  = Matrix<Index , 1, 3>;
using RowVector4i  = Matrix<Index , 1, 4>;



/* "ArrayNN" is simply "Array", so not needed */

template<Index rows, Index cols>
using ArrayNNs = Array<scalar, rows, cols   >;
template<Index rows, Index cols>
using ArrayNNi = Array<Index , rows, cols   >;


template<typename T, Index rows>
using ArrayNX  = Array<T     , rows, Dynamic>;
template<Index rows>
using ArrayNXs = Array<scalar, rows, Dynamic>;
template<Index rows>
using ArrayNXi = Array<Index , rows, Dynamic>;


template<typename T>
using ArrayXX  = Array<T     , Dynamic, Dynamic>;

using ArrayXXs = Array<scalar, Dynamic, Dynamic>;
using ArrayXXi = Array<Index , Dynamic, Dynamic>;

template<typename T>
using Array1X  = Array<T     , 1, Dynamic>;
template<typename T>
using Array2X  = Array<T     , 2, Dynamic>;
template<typename T>
using Array3X  = Array<T     , 3, Dynamic>;
template<typename T>
using Array4X  = Array<T     , 4, Dynamic>;

using Array1Xs = Array<scalar, 1, Dynamic>;
using Array2Xs = Array<scalar, 2, Dynamic>;
using Array3Xs = Array<scalar, 3, Dynamic>;
using Array4Xs = Array<scalar, 4, Dynamic>;

using Array1Xi = Array<Index , 1, Dynamic>;
using Array2Xi = Array<Index , 2, Dynamic>;
using Array3Xi = Array<Index , 3, Dynamic>;
using Array4Xi = Array<Index , 4, Dynamic>;

template<Index cols>
using Array1Ns = Array<scalar, 1, cols>;
template<Index cols>
using Array2Ns = Array<scalar, 2, cols>;
template<Index cols>
using Array3Ns = Array<scalar, 3, cols>;
template<Index cols>
using Array4Ns = Array<scalar, 4, cols>;

template<Index cols>
using Array1Ni = Array<Index , 1, cols>;
template<Index cols>
using Array2Ni = Array<Index , 2, cols>;
template<Index cols>
using Array3Ni = Array<Index , 3, cols>;
template<Index cols>
using Array4Ni = Array<Index , 4, cols>;


template<typename T, Index cols>
using ArrayXN  = Array<T     , Dynamic, cols>;
template<Index cols>
using ArrayXNi = Array<Index , Dynamic, cols>;
template<Index cols>
using ArrayXNs = Array<scalar, Dynamic, cols>;

template<typename T>
using ArrayX2  = Array<T     , Dynamic, 2>;
template<typename T>
using ArrayX3  = Array<T     , Dynamic, 3>;
template<typename T>
using ArrayX4  = Array<T     , Dynamic, 4>;

using ArrayX2s = Array<scalar, Dynamic, 2>;
using ArrayX3s = Array<scalar, Dynamic, 3>;
using ArrayX4s = Array<scalar, Dynamic, 4>;

using ArrayX2i = Array<Index , Dynamic, 2>;
using ArrayX3i = Array<Index , Dynamic, 3>;
using ArrayX4i = Array<Index , Dynamic, 4>;


template<typename T, Index rows>
using ArrayN   = Array<T     , rows, 1>;

template<Index rows>
using ArrayNs  = Array<scalar, rows, 1>;

template<Index rows>
using ArrayNi  = Array<Index , rows, 1>;


template<typename T>
using ArrayX   = Array<T     , Dynamic, 1>;

using ArrayXs  = Array<scalar, Dynamic, 1>;

using ArrayXi  = Array<Index , Dynamic, 1>;

template<typename T>
using Array1   = Array<T     , 1, 1>;
template<typename T>
using Array2   = Array<T     , 2, 1>;
template<typename T>
using Array3   = Array<T     , 3, 1>;
template<typename T>
using Array4   = Array<T     , 4, 1>;

using Array1s  = Array<scalar, 1, 1>;
using Array2s  = Array<scalar, 2, 1>;
using Array3s  = Array<scalar, 3, 1>;
using Array4s  = Array<scalar, 4, 1>;

using Array1i  = Array<Index , 1, 1>;
using Array2i  = Array<Index , 2, 1>;
using Array3i  = Array<Index , 3, 1>;
using Array4i  = Array<Index , 4, 1>;


template<typename T, Index rows>
using ArrayN   = Array<T     , rows, 1>;

template<Index rows>
using ArrayNs  = Array<scalar, rows, 1>;

template<Index rows>
using ArrayNi  = Array<Index , rows, 1>;


template<typename T>
using RowArrayX   = Array<T     , 1, Dynamic>;

using RowArrayXs  = Array<scalar, 1, Dynamic>;

using RowArrayXi  = Array<Index , 1, Dynamic>;

template<typename T>
using RowArray1   = Array<T     , 1, 1>;
template<typename T>
using RowArray2   = Array<T     , 1, 2>;
template<typename T>
using RowArray3   = Array<T     , 1, 3>;
template<typename T>
using RowArray4   = Array<T     , 1, 4>;

using RowArray1s  = Array<scalar, 1, 1>;
using RowArray2s  = Array<scalar, 1, 2>;
using RowArray3s  = Array<scalar, 1, 3>;
using RowArray4s  = Array<scalar, 1, 4>;

using RowArray1i  = Array<Index , 1, 1>;
using RowArray2i  = Array<Index , 1, 2>;
using RowArray3i  = Array<Index , 1, 3>;
using RowArray4i  = Array<Index , 1, 4>;

template<Index rows>
using ArrayNXsMap = Eigen::Map<ArrayNXs<rows>>;
using Array1XsMap = ArrayNXsMap<1>;
using Array2XsMap = ArrayNXsMap<2>;
using Array3XsMap = ArrayNXsMap<3>;
using Array4XsMap = ArrayNXsMap<4>;

template<Index rows>
using ArrayNXsCMap = Eigen::Map<const ArrayNXs<rows>>;
using Array1XsCMap = ArrayNXsCMap<1>;
using Array2XsCMap = ArrayNXsCMap<2>;
using Array3XsCMap = ArrayNXsCMap<3>;
using Array4XsCMap = ArrayNXsCMap<4>;

template<Index cols>
using ArrayXNsMap = Eigen::Map<ArrayXNs<cols>>;
using ArrayX1sMap = ArrayXNsMap<1>;
using ArrayX2sMap = ArrayXNsMap<2>;
using ArrayX3sMap = ArrayXNsMap<3>;
using ArrayX4sMap = ArrayXNsMap<4>;


using ArrayXXsMap = Eigen::Map<ArrayXXs>;
using ArrayXXsCMap = Eigen::Map<const ArrayXXs>;

using IdxVector = std::vector<Index>;
	
/* the Hyperplane class is a good representation of infinite lines and 
 * provides methods like normal(), which are not available from the 
 * ParametrizedLine class. The latter is more useful for finite lines,
 * such as for calculating intersecting line segments rather than infinite
 * lines. They can be converted into each other. */
using Line = Eigen::Hyperplane<scalar, hms::dims>;
using ParamLine = Eigen::ParametrizedLine<scalar, hms::dims>;

/* axis aligned boxes are only needed in 2D */
using AlignedBox = Eigen::AlignedBox<scalar, hms::dims>;

/* this type is used in many places, unchanged, and a bit long */
using TimeSeries = std::vector<std::pair<scalar, ArrayXXs>>;

}

#endif